﻿using System;

namespace MiniGert
{
  /// <summary>
  /// Circle is a simple circle primitive. There are methods for various
  /// math like collision detection and such thing. When the circle is drawn
  /// it is adaptivly tesselated to a polyline.
  /// </summary>
  public class Circle
  {
    private Point _center = null;
    private float _radius = 1.0f;
    private float _pi = (float)Math.PI;

    /// <summary>
    /// Creates a new Circle centered at (0,0) with a radius of 1.
    /// </summary>
    public Circle()
    {
      _center = new Point();
    }

    /// <summary>
    /// Creates a new circle centered at the given center with the given radius.
    /// </summary>
    /// <param name="center">The center of the circle.</param>
    /// <param name="radius">The radius of the circle.</param>
    public Circle(Point center, float radius)
    {
      _center = center;
      _radius = radius;
    }

    /// <summary>
    /// Creates a new circle centered at the given coordinates with the given radius.
    /// </summary>
    /// <param name="x">The x coordinate of the center.</param>
    /// <param name="y">The y coordinate of the center.</param>
    /// <param name="radius">The raius of the circle.</param>
    public Circle(float x, float y, float radius)
    {
      _center = new Point(x, y);
      _radius = radius;
    }

    /// <summary>
    /// Gets or sets the center of the circle.
    /// </summary>
    public Point Center
    {
      get { return _center; }
      set { _center = value; }
    }

    /// <summary>
    /// Gets or sets the radius of the center.
    /// </summary>
    public float Radius
    {
      get { return _radius; }
      set { _radius = value; }
    }

    /// <summary>
    /// Checks if the given point is inside of the circle.
    /// </summary>
    /// <param name="x">The x component of the point.</param>
    /// <param name="y">The y component of the point.</param>
    /// <returns>True if the point lies inside of or on the border of the circle, else False.</returns>
    public bool PointIsInside(float x, float y)
    {
      return PointIsInside(new Point(x, y));
    }

    /// <summary>
    /// Checks if the given point is inside of the circle.
    /// </summary>
    /// <param name="point">The point to be checked.</param>
    /// <returns>True if the point lies inside of or on the border of the circle, else False.</returns>
    /// <returns></returns>
    public bool PointIsInside(Point point)
    {
      return _center.DistanceTo(point) <= _radius;
    }

    /// <summary>
    /// Calculates the closest point from the given point on the "surface" of the circle.
    /// </summary>
    /// <param name="point">A point.</param>
    /// <returns>The closest point on the surface of the circle to the given point.</returns>
    public Point ClosestPoint(Point point)
    {
      Vector vec = new Vector(point.X - _center.X, point.Y - _center.Y);
      vec.Normalize();
      return Point.Transform(_center, vec, _radius);
    }

    /// <summary>
    /// Calculates the circumference of the circle.
    /// </summary>
    /// <returns>The circumference of the circle.</returns>
    public float Circumference()
    {
      return 2 * _pi * _radius;
    }

    /// <summary>
    /// Gets the bounding rectangle of the circle.
    /// </summary>
    /// <returns>The bounding rectangle of the circle</returns>
    public Rectangle Bounds()
    {
      return new Rectangle(_center.X - _radius, _center.Y - _radius, _center.X + _radius, _center.Y + _radius);
    }

  }
}
